package laos.src;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Point;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;

public class GameActivity extends Activity {
	public final int MAX_PLAYERS = 4;

	private AnimationView a;
	private CameraView cam;
	private int[] borders;
	/** A boolean indicates whether to send location **/
	private boolean sendLoc;
	private String hostName;
	private int portNum;
	/** The thread that updates the game state */
	private CommandListener connectionThread;
	/** The Players */
	private ArrayList<Player> players;
	private Handler mHandler;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
				WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
		mHandler = new Handler();
		/** Initiate player list */
		players = new ArrayList<Player>();
		players.add(new Player(Color.GREEN, Color.rgb(0, 150, 0), Color.rgb(0,
				200, 0)));
		players.add(new Player(Color.RED, Color.rgb(150, 0, 0), Color.rgb(200,
				0, 0)));
		players.add(new Player(Color.BLUE, Color.rgb(0, 0, 150), Color.rgb(0,
				0, 200)));
		a = new AnimationView(this, players);
		cam = new CameraView(this);
		setContentView(cam);
		addContentView(a, new LayoutParams(LayoutParams.WRAP_CONTENT,
				LayoutParams.WRAP_CONTENT));
		Bundle extras = getIntent().getExtras();
		boolean isGameOwner = false;
		if (extras != null) {
			isGameOwner = extras.getBoolean("IS_GAME_OWNER");
			if (isGameOwner) {
				int[] borders = new int[8];
				borders = extras.getIntArray("BORDERS");// get("BORDERS");
				setBorders(borders);
			}
		}
		gpsSetup();
		int portNum = Integer.valueOf(extras.getString("PORT"));
		setServerAddr(extras.getString("IP"), portNum);
		connectionThread = new CommandListener();
		sendLoc = false;
		connectionThread.setRunning(true);
		connectionThread.start();
	}

	@Override
	protected void onPause() {
		super.onPause();
		sendLoc = false;
		connectionThread.setRunning(false);
		boolean retry = true;
		while (retry) {
			try {
				connectionThread.join();
				retry = false;
			} catch (InterruptedException e) {
			}
		}
	}

	private final LocationListener locationListener = new LocationListener() {
		public void onLocationChanged(Location location) {
			// locationstr = location.getLatitude() + ", " +
			// location.getLongitude();
			if (a != null) {
				if (sendLoc) {
					sendLocation((int) (location.getLatitude() * 1E6),
							(int) (location.getLongitude() * 1E6));
				}
			}
		}

		public void onProviderDisabled(String provider) {
		}

		public void onProviderEnabled(String provider) {
		}

		public void onStatusChanged(String provider, int status, Bundle extras) {
		}
	};

	private void gpsSetup() {
		LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		Criteria criteria = new Criteria();
		criteria.setAccuracy(Criteria.ACCURACY_FINE);
		criteria.setAltitudeRequired(false);
		criteria.setBearingRequired(false);
		criteria.setCostAllowed(true);
		String provider = locationManager.getBestProvider(criteria, true);
		// Location location = locationManager.getLastKnownLocation(provider);
		// markLocationOnMap(location);
		locationManager.requestLocationUpdates(provider, 2000, 1,
				locationListener);// THIS IS IMPORTENT!
	}

	class CommandListener extends Thread {
		boolean running;

		DataOutputStream out;
		DataInputStream input;
		InetSocketAddress serverAddr;
		Socket s;

		public Point[] STARTING_LOCATIONS;

		private void readAll(DataInputStream in, byte[] buff, int off, int len)
				throws IOException {
			int count = 0;
			do {
				int ret = in.read(buff, off + count, len);
				if (ret == -1)
					throw new IOException();
				count += ret;
			} while (count < len);
		}

		public CommandListener() {
			serverAddr = null;
			out = null;
			input = null;
			s = null;
			try {
				s = new Socket();
				serverAddr = new InetSocketAddress(
						InetAddress.getByName(hostName), portNum);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				setRunning(false);
			}

			/**
			 * Allocate space for starting locations Must initiate each cell in
			 * the initiatePlayersLocation function
			 */
			STARTING_LOCATIONS = new Point[MAX_PLAYERS];
		}

		public void setRunning(boolean b) {
			running = b;
		}

		public void sendLocation(int a, int b) {
			try {
				out.writeInt(a);
				out.writeInt(b);
				out.flush();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		@Override
		public void run() {
			try {
				s.connect(serverAddr);
				out = new DataOutputStream(s.getOutputStream());
				input = new DataInputStream(s.getInputStream());
				out.writeByte('C');
				if (borders != null) {
					for (int i = 0; i < borders.length; i += 2) {
						out.writeInt(borders[i]);
						out.writeInt(borders[i + 1]);
					}
					out.flush();
				}
				byte[] ans = new byte[13];
				while (running && s != null && s.isConnected()) {
					try {
						readAll(input, ans, 0, 1);
						int p = 0, x = 0, y = 0;
						switch (ans[0]) {
						case (char) 0:
							resetGame();
							break;
						case (char) 1:
							readAll(input, ans, 1, 12);
							p = byteBuffToInt(ans, 1, 4);
							x = byteBuffToInt(ans, 5, 4);
							y = byteBuffToInt(ans, 9, 4);
							// x *= 10;
							// y *= 10;
							addPointToTail(p, x, y);
							break;
						case (char) 2:
							readAll(input, ans, 1, 4);
							p = byteBuffToInt(ans, 1, 4);
							resetTail(p);
							break;
						case (char) 3:
							readAll(input, ans, 1, 12);
							p = byteBuffToInt(ans, 1, 4);
							x = byteBuffToInt(ans, 5, 4);
							y = byteBuffToInt(ans, 9, 4);
							// x *= 10;
							// y *= 10;
							addPointToArea(p, x, y);
							break;
						case (char) 4:
							readAll(input, ans, 1, 12);
							p = byteBuffToInt(ans, 1, 4);
							x = byteBuffToInt(ans, 5, 4);
							y = byteBuffToInt(ans, 9, 4);
							changeLoc(p, x, y);
							break;
						case (char) 5:
							readAll(input, ans, 1, 8);
							int arrWidth = byteBuffToInt(ans, 1, 4);
							int arrHeight = byteBuffToInt(ans, 5, 4);
							a.text = "Playing";
							a.setRatio(arrWidth, arrHeight);
							sendLoc = true;
							break;
						}
						mHandler.post(new Runnable() {
						    public void run()
						    {
						    	a.update(players);
						    }
						});
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
						setRunning(false);
					}
				}
				try {
					if (s != null)
						s.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				input = null;
				out = null;
				s = null;
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				setRunning(false);
			}
		}

		private int byteBuffToInt(byte[] buff, int offset, int len) {
			int value = 0;
			for (int i = offset; i - offset < len; i++) {
				value = (value << 8) + (buff[i] & 0xff);
			}
			return value;
		}
	}

	public void setServerAddr(String host, int port) {
		hostName = host;
		portNum = port;
	}

	public void sendLocation(int a, int b) {
		connectionThread.sendLocation(a, b);
	}

	public void setBorders(int borders[]) {
		this.borders = borders;
	}
	void resetGame() throws Exception {
		if (players.size() > MAX_PLAYERS) {
			throw new Exception("ERROR: Max number of players exceeded");
		}
		synchronized (players) {
			Iterator<Player> it = players.iterator();
			int i = 0;
			while (it.hasNext()) {
				Player p = it.next();
				p.resetPlayer();
				++i;
			}
		}
	}

	void resetTail(int p) {
		synchronized (players) {
			if (p <= players.size() && p > 0)
				players.get(p - 1).resetTail();
		}
	}

	void addPointToTail(int p, int x, int y) {
		synchronized (players) {
			if (p <= players.size() && p > 0)
				players.get(p - 1).addPointToTail(x, y);
		}
	}

	void addPointToArea(int p, int x, int y) {
		synchronized (players) {
			if (p <= players.size() && p > 0)
				players.get(p - 1).addPointToArea(x, y);
		}
	}

	void changeLoc(int p, int x, int y) {
		synchronized (players) {
			if (p <= players.size() && p > 0)
				players.get(p - 1).changeLoc(x, y);
		}
	}
}